package eval;

import config.Config;
import earlyDetection.EarlyDetector;
import network.Device;
import network.Network;
import network.Rule;
import verifier.JiffyVerifier;
import verifier.Ports.PersistentPorts;
import verifier.util.Changes;
import verifier.util.PPM;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class MCI implements Runnable {
    private Network network;
    private List<Rule> rules;
    private JiffyVerifier verifier;
    private BlockingQueue<Rule> blockingQueue;
    private boolean enableEarlyDetection = false;
    private EarlyDetector earlyDetector;

    public MCI(Network network, BlockingQueue<Rule> blockingQueue) {
        this.network = network;
        this.blockingQueue = blockingQueue;
        this.verifier = new JiffyVerifier(network, new PersistentPorts());
        this.earlyDetector = new EarlyDetector();
    }

    public void run(boolean useFFMT) {
        long s = System.nanoTime();
        if (useFFMT) {
            Changes changes = verifier.insertMiniBatch(rules);
            this.verifier.update(changes);
        } else {
            for (Rule rule : rules) {
                Changes changes = verifier.insertMiniBatch(new ArrayList<>(Collections.singletonList(rule)));
                this.verifier.update(changes);
            }
        }
        long time = System.nanoTime() - s;
        System.out.println("useFFMT: " + useFFMT);
        System.out.println("$model: " + time);
        System.out.println("#rules: " + rules.size());
        System.out.println("#ECs: " + verifier.predSize());
    }

    @Override
    public void run() {
        List<Rule> rules = new ArrayList<>();
        while (true) {
            try {
                rules.add(blockingQueue.take());
                int n = 1;
                n += blockingQueue.drainTo(rules);
                boolean containClose = false;
                HashSet<Device> newClosed = new HashSet<>();
                for (int i = 0; i < n; i++) {
                    Rule rule = rules.get(rules.size() - 1 - i);
                    if (rule.isLast()) {
                        containClose = true;
                        newClosed.add(rule.getDevice());
                    }
                }

                Changes changes = verifier.insertMiniBatch(rules);
                this.verifier.update(changes);

                rules.clear();

//                this.verifier.printTime(0);
//                System.out.println(this.verifier.predSize());

                if (enableEarlyDetection && containClose) {
                    PPM ppm = new PPM(verifier.portsToPredicate);
//                    earlyDetector.detectLoop(newClosed, ppm.getPortToPreds());
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void setEnableEarlyDetection(boolean enableEarlyDetection) {
        this.enableEarlyDetection = enableEarlyDetection;
    }
}
